package com.seahorse.youliao.security.sms;

import com.seahorse.youliao.security.GrantedAuthorityImpl;
import com.seahorse.youliao.security.JwtUserDetails;
import com.seahorse.youliao.service.SysMenuService;
import com.seahorse.youliao.service.SysUserService;
import com.seahorse.youliao.service.entity.SysUserDTO;
import com.seahorse.youliao.utils.SpringContextUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.core.authority.mapping.NullAuthoritiesMapper;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

import java.util.List;
import java.util.stream.Collectors;

/**
 * @description: 短信登录身份认证组件
 * @author: Mr.Song
 * @create: 2020-11-23 21:25
 **/
@Slf4j
public class SmsCodeAuthenticationProvider implements AuthenticationProvider {


    private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {


        String mobile = (String) authentication.getPrincipal();

        //根据手机号加载用户
        UserDetails user = loadUserByPhone(mobile);

        Object principalToReturn = user;
        return createSuccessAuthentication(principalToReturn, authentication, user);
    }

    @Override
    public boolean supports(Class<?> aClass) {
        //如果是SmsCodeAuthenticationToken该类型，则在该处理器做登录校验
        return SmsCodeAuthenticationToken.class.isAssignableFrom(aClass);
    }


    /**
     * 跟账号登录保持一致
     * @param principal
     * @param authentication
     * @param user
     * @return
     */
    protected Authentication createSuccessAuthentication(Object principal,
                                                         Authentication authentication, UserDetails user) {
        // Ensure we return the original credentials the user supplied,
        // so subsequent attempts are successful even with encoded passwords.
        // Also ensure we return the original getDetails(), so that future
        // authentication events after cache expiry contain the details
        UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(
                principal, authentication.getCredentials(),
                authoritiesMapper.mapAuthorities(user.getAuthorities()));
        result.setDetails(authentication.getDetails());

        return result;
    }

    /**
     * 获取用户信息
     * @param phone
     * @return
     * @throws UsernameNotFoundException
     */
    public UserDetails loadUserByPhone(String phone) throws UsernameNotFoundException {

        SysUserService sysUserService = (SysUserService)SpringContextUtils.getBeanByClass(SysUserService.class);
        SysUserDTO user = sysUserService.findByPhone(phone);
        if (user == null) {
            throw new UsernameNotFoundException("该手机号不存在");
        }

        // 用户权限列表，根据用户拥有的权限标识与如 @PreAuthorize("hasAuthority('sys:menu:view')") 标注的接口对比，决定是否可以调用接口
        SysMenuService sysMenuService = (SysMenuService)SpringContextUtils.getBeanByClass(SysMenuService.class);
        List<String> permissions = sysMenuService.getPermissionsByUserId(user.getId());
        log.info("用户权限标识 permissions = {}",permissions);
        List<GrantedAuthority> grantedAuthorities = permissions.stream().map(GrantedAuthorityImpl::new).collect(Collectors.toList());
        return new JwtUserDetails(user.getUserName(), user.getPassword(),user.getEnabled(), grantedAuthorities);
    }
}
